package com.chaoxinet.csmall.passport.exception.handler;


import com.chaoxinet.csmall.passport.exception.ServiceException;
import com.chaoxinet.csmall.passport.web.JsonResult;
import com.chaoxinet.csmall.passport.web.ServiceCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;

/**
 * 统一处理异常
 *
 * @author java@tedu.cn
 * @version 0.0.1
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    public GlobalExceptionHandler() {
        log.debug("创建统一处理异常的对象：GlobalExceptionHandler");
    }

    /**
     * 处理ServiceException异常
     *
     * @param e ServiceException异常对象
     * @return 返回JsonResult<Void>对象
     */
    @ExceptionHandler
    public JsonResult<Void> handleServiceException(ServiceException e) {
        log.warn("程序运行过程中出现ServiceException,将统一处理");
        log.warn("异常信息:{}", e.getMessage());
        return JsonResult.fail(e);
    }

    /**
     * 处理BindException异常
     * @param e BindException异常对象
     * @return JsonResult<Void>对象
     */
    @ExceptionHandler
    public JsonResult<Void> handleBindException(BindException e) {
        log.warn("程序运行过程中出现BindException,将统一处理");
        log.warn("异常信息:{}", e.getMessage());
        // 可以解决使用getMessage获取异常信息的问题
        return JsonResult.fail(ServiceCode.ERR__BAD_REQUEST, e.getFieldError().getDefaultMessage());
    }


    /**
     * 处理ConstraintViolationException异常
     * @param e ConstraintViolationException异常对象
     * @return JsonResult<Void>对象
     */
    @ExceptionHandler
    public JsonResult<Void> handleConstraintViolationException(ConstraintViolationException e) {
        log.warn("程序运行过程中出现ConstraintViolationException,将统一处理");
        log.warn("异常信息:{}", e.getMessage());
        // 由于ConstraintViolationException的API设计,只能拿到"所有错误"的集合,才能进一步拿到错误的提示文本
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        String message = "";
        for (ConstraintViolation<?> constraintViolation : constraintViolations) {
            message = constraintViolation.getMessage();
        }
        return JsonResult.fail(ServiceCode.ERR__BAD_REQUEST, message);
    }



    /**
     * 处理AuthenticationException异常
     * @param e AuthenticationException异常对象
     * @return JsonResult<Void>对象
     */
    @ExceptionHandler({InternalAuthenticationServiceException.class, BadCredentialsException.class})
    public JsonResult<Void> handleBadCredentialsException(AuthenticationException e) {
        log.warn("程序运行过程中出现AuthenticationException,将统一处理");
        log.warn("异常信息:{}", e.getMessage());
        String message = "登录失败,用户名或密码错误!";
        return JsonResult.fail(ServiceCode.ERR_UNAUTHORIZED, message);
    }

    /**
     * 处理禁用异常的处理器
     */
    @ExceptionHandler
    public JsonResult<Void> handleDisabledException(DisabledException e) {
        log.warn("程序运行过程中出现BadCredentialsException,将统一处理");
        log.warn("异常信息:{}", e.getMessage());
        String message = "登录失败,此账号已经被禁用";
        return JsonResult.fail(ServiceCode.ERR_UNAUTHORIZED_DISABLED, message);
    }

    @ExceptionHandler
    public JsonResult<Void> handleAccessDeniedExceptionException(AccessDeniedException e) {
        log.warn("程序运行过程中出现AccessDeniedException,将统一处理");
        log.warn("异常信息:{}", e.getMessage());
        String message = "登录失败,当前账号没有此操作的权限";
        return JsonResult.fail(ServiceCode.ERR_FORBIDDEN, message);
    }


    // 注意: 以下方法存在的意义主要在于:避免某个异常没有被处理,导致服务器端响应500错误
    // 注意: e.printStackTrace()通常是禁止使用的!,应为其输出方式是阻塞的式的;
    // 以下方法中使用了此语句,是因为用于发现错误,并不断的补充处理对应的异常的方法
    // 随着项目开发进度的推进,执行到意向方法的概率会越来越低
    // 出先由于此语句导致的问题的概率也会越来越低
    // 甚至补充足够多的处理异常的方法后,根本不会直行到以下的方法了
    // 当项目上线后,此方法将不会被调用,可以直接删除
    /**
     * 处理服务异常的处理器
     */
    @ExceptionHandler
    public JsonResult<Void> handleServiceException(Throwable e) {
        log.warn("程序运行过程中出现BinException,将统一处理");
        log.warn("异常信息:{}", e.getClass().getName());
        log.warn("异常信息:{}", e.getMessage());
        String message = "服务器忙,请稍后在尝试!"; // 服项目上线时的,提示文本
        e.printStackTrace();

        return JsonResult.fail(ServiceCode.ERR_UNKNOWN, message);
    }

}
